package butines.game.lastcolony;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.util.LinkedList;
import java.util.List;

import butines.core.Mouse;
import butines.core.gui.GUIComponent;
import butines.core.gui.GUIListener;
import butines.core.gui.MouseEvent;
import butines.game.lastcolony.entities.Aircraft;
import butines.game.lastcolony.entities.Building;
import butines.game.lastcolony.entities.Entity;
import butines.game.lastcolony.entities.EntityOrders;
import butines.game.lastcolony.entities.Game;
import butines.game.lastcolony.entities.GameCommand;
import butines.game.lastcolony.entities.Terrain;
import butines.game.lastcolony.entities.Vehicle;

public class GameCanvas extends GUIComponent implements GUIListener {

    // x,y coordinates of mouse relative to top left corner of canvas
    private int mouseX, mouseY;
    
    // x,y coordinates of mouse relative to top left corner of game map
    private int gameX, gameY;
    
    private int dragX, dragY;

    private boolean dragSelect;
    
    // game grid x,y coordinates of mouse 
    private int gridX, gridY;

    private boolean insideCanvas;
    
    private Game game;
    private SinglePlayer singlePlayer;
    private MultiPlayer multiPlayer;
    
    public GameCanvas(int x, int y, LastColony lastColony) {
        game = lastColony.getGame();
        singlePlayer = lastColony.getSinglePlayer();
        multiPlayer = lastColony.getMultiPlayer();
        setPosition(x, y);
        setSize(game.getCanvasWidth(), game.getCanvasWidth());
        setGuiListener(this);
    }

    @Override
    public void onMouseDown(MouseEvent e) {
        dragX = gameX;
        dragY = gameY;
        dragSelect = false;
    }
    
    private boolean between(float value, float min, float max) {
        return min <= value && max >= value;
    }
    
    private boolean canMultipleSelection(Entity item) {
       return (item instanceof Vehicle || item instanceof Aircraft)
           && item.isSelectable()
           && item.getTeam() == game.getTeam();
    }
    
    
    @Override
    public void onMouseUp(MouseEvent e) {
        if (e.isButton1()) {
            if (dragSelect) {

                if (!e.isShift()) {
                    // Shift key was not pressed
                    game.clearSelection();
                }

                int x1 = Math.min(gameX, dragX) / game.getGridSize();
                int y1 = Math.min(gameY, dragY) / game.getGridSize();
                int x2 = Math.max(gameX, dragX) / game.getGridSize();
                int y2 = Math.max(gameY, dragY) / game.getGridSize();

                for (int i = game.getItems().size() - 1; i >= 0; i--) {
                    Entity item = game.getItems().get(i);
                    float x = item.getX();
                    float y = item.getY();
                    int h = item.getPixelShadowHeight() / game.getGridSize();
                    if (canMultipleSelection(item) && between(x, x1, x2) && between(y, y1, y2 + h)) {
                        game.selectItem(item, e.isShift());
                    }
                }
                
            }
            dragSelect = false;
        }
    }

    @Override
    public void onClick(MouseEvent e) {
    	click(e);
    }

    @Override
    public void onMouseEnter(MouseEvent e) {
        insideCanvas = true;
    }

    @Override
    public void onMouseLeave(MouseEvent e) {
        insideCanvas = false;
    }

    @Override
    public void onMouseMove(MouseEvent e) {
        mouseX = e.getMouseX() - getX();
        mouseY = e.getMouseY() - getY();
        
        calculateGameCoordinates();
        
        if (e.isButton1()) {
            if ((Math.abs(dragX - gameX) > 4 || Math.abs(dragY - gameY) > 4)) {
                dragSelect = true;
            }
        } else {
            dragSelect = false;
        }   
     
    }
    
    
    private float timeCount;
    
    @Override
    protected void updateComponent(Mouse mouse, float delta) {
        timeCount += delta;

        final float timeTick = 1f / game.getGameSpeed();
        while (timeCount >= timeTick) {
            timeCount -= timeTick;
            if (insideCanvas) {
                game.handlePanning(mouseX, mouseY);
            }
            game.animationLoop();
        }
        
    }

    @Override
    protected void renderComponent(Graphics2D g) {
        Graphics2D g2d = (Graphics2D) g.create(getX(), getY(), getWidth(), getHeight());
        g2d.setFont(new Font("Dialog", Font.PLAIN, 9)); // XXX: Debug
        
        game.drawingLoop(g2d);
        drawDragSelect(g2d);

        /*
        g2d.setColor(new Color(255,255,0,128));
        g2d.fillRect((gridX * game.getGridSize()) - game.getOffsetX() , (gridY * game.getGridSize()) - game.getOffsetY(), game.getGridSize(), game.getGridSize());

        Entity mouseHover = itemUnderMouse();
        if (mouseHover != null) {
            if (mouseHover instanceof Building || mouseHover instanceof Terrain) {
            	int x1 = (mouseHover.getX() * game.getGridSize()) - game.getOffsetX();
            	int y1 = (mouseHover.getY() * game.getGridSize()) - game.getOffsetY();
            	int w = mouseHover.getBaseWidth();
            	int h = mouseHover.getBaseHeight();
            	if (mouseHover instanceof Building) {
            		g2d.setColor(new Color(255,0,255,128));
            	} else {
                    g2d.setColor(new Color(255,255,255,128));
            	}
                g2d.fillRect(x1, y1, w, h);
            } else if (mouseHover instanceof Vehicle) {
            	int x1 = (mouseHover.getX() * game.getGridSize()) - game.getOffsetX() - mouseHover.getRadius();
            	int y1 = (mouseHover.getY() * game.getGridSize()) - game.getOffsetY() - mouseHover.getRadius();
            	int w = mouseHover.getRadius()*2;
            	int h = mouseHover.getRadius()*2;
                g2d.setColor(new Color(255,175,175,128));
                g2d.fillOval(x1, y1, w, h);
            } else if (mouseHover instanceof Aircraft) {
            	int x1 = (mouseHover.getX() * game.getGridSize()) - game.getOffsetX() - mouseHover.getRadius();
            	int y1 = (mouseHover.getY() * game.getGridSize()) - game.getOffsetY() - mouseHover.getRadius() - mouseHover.getPixelShadowHeight();
            	int w = mouseHover.getRadius()*2;
            	int h = mouseHover.getRadius()*2;
            	g2d.setColor(new Color(255,200,0,128));
                g2d.fillOval(x1, y1, w, h);
            }
        }
        
        String str =  "x:" + mouseX + " y:" + mouseY + "[" + gridX + "," + gridY + "]";
        int wstr = g2d.getFontMetrics().stringWidth(str);
        g2d.setColor(Color.WHITE);
        g2d.drawString(str, mouseX - wstr/2, mouseY);*/
        
        g2d.dispose();
    }

    private void calculateGameCoordinates() {
        gameX = mouseX + game.getOffsetX();
        gameY = mouseY + game.getOffsetY();

        gridX = gameX / game.getGridSize();
        gridY = gameY / game.getGridSize();    
    }
    
    private void drawDragSelect(Graphics2D g2d) {
        if(dragSelect){    
            int x = Math.min(gameX, dragX) - game.getOffsetX();
            int y = Math.min(gameY, dragY) - game.getOffsetY();
            int w = Math.abs(gameX - dragX);
            int h = Math.abs(gameY - dragY);
            g2d.setStroke(new BasicStroke(2));
            g2d.setColor(new Color(255, 255, 255, 128));
            g2d.drawRect(x, y, w, h);
        }
    }

    private Entity itemUnderMouse(){
    	for (int i = game.getItems().size() - 1; i >= 0; i--) {
	    	Entity item = game.getItems().get(i);
	    	if (item instanceof Building || item instanceof Terrain) {
	    	    int x1 = gameX / game.getGridSize();
	    	    int y1 = gameY / game.getGridSize();
	    	    int x2 = ((gameX - item.getBaseWidth()) / game.getGridSize());
	    	    int y2 = ((gameY - item.getBaseHeight()) / game.getGridSize());
	    		if(!item.isDead()
	    		        && item.getX() <= x1 && item.getX() > x2
	    		        && item.getY() <= y1 && item.getY() > y2) {
	    			return item;
	    		}
	    	} else if (item instanceof Aircraft) {
	    		float gridSize = (float) game.getGridSize();
	    		float x = item.getX() - gameX / gridSize;
	    		float y = item.getY() - (gameY + item.getPixelShadowHeight()) / gridSize;
	    		float r = item.getRadius() / gridSize;
	    		if (!item.isDead() && (x*x + y*y) < (r*r)) {
	    			return item;
	    		}
	    	} else {
	    		float gridSize = (float) game.getGridSize();
	    		float x = item.getX() - gameX / gridSize;
	    		float y = item.getY() - gameY / gridSize;
	    		float r = item.getRadius() / gridSize;
	    		if (!item.isDead() && (x*x + y*y) < (r*r)) {
	    			return item;
	    		}
	    	}
	    }
    	return null;
	}
    
    private void click(MouseEvent e) {
    	// Player clicked inside the canvas
        Entity clickedItem = itemUnderMouse();
    	if (e.isButton1()){ // Player left clicked
    		if (clickedItem != null) {
    			// Pressing shift adds to existing selection. If shift is not pressed, clear existing selection
    			if(!e.isShift()){
    				game.clearSelection();
    			}
    			game.selectItem(clickedItem, e.isShift());
    		}
    	} else if (e.isButton3()) { // Player right clicked
            // Handle actions like attacking and movement of selected units
            List<Integer> uids = new LinkedList<Integer>();
            if (clickedItem != null) { // Player right-clicked on something
                if (!(clickedItem instanceof Terrain)) {
                    if (clickedItem.getTeam() != game.getTeam()) { // Player right-clicked on an enemy item
                        // Identify selected items from players team that can attack
                        for (int i = game.getSelectedItems().size() - 1; i >= 0; i--) {
                            Entity item = game.getSelectedItems().get(i);
                            if (item.getTeam() == game.getTeam() && item.isCanAttack()) {
                                uids.add(item.getUID());
                            }
                        }
                        // then command them to attack the clicked item
                        if (!uids.isEmpty()) {
                            game.sendCommand(uids, new GameCommand(EntityOrders.Type.ATTACK, clickedItem.getUID()));
                        }
                    } else { // Player right-clicked on a friendly item
                        // identify selected items from players team that can move
                        for (int i = game.getSelectedItems().size() - 1; i >= 0; i--) {
                            Entity item = game.getSelectedItems().get(i);
                            if (item.getTeam() == game.getTeam() && (item instanceof Vehicle || item instanceof Aircraft)) {
                                uids.add(item.getUID());
                            }
                        }
                        // then command them to guard the clicked item
                        if (!uids.isEmpty()) {
                            game.sendCommand(uids, new GameCommand(EntityOrders.Type.GUARD, clickedItem.getUID()));
                        }
                    }
                } else if (clickedItem.getEntityClass() == Terrain.OILFIELD) { // Player right licked on an oilfield
                    // identify the first selected harvester from players team (since only one can deploy at a time)
                    for (int i = game.getSelectedItems().size() - 1; i >= 0; i--) {
                        Entity item = game.getSelectedItems().get(i);
                        if (item.getTeam() == game.getTeam() && (item instanceof Vehicle && item.getEntityClass() == Vehicle.HARVESTER)) {
                            uids.add(item.getUID());
                            break;
                        }
                    }
                    // then command it to deploy on the oilfield
                    if (!uids.isEmpty()) {
                        game.sendCommand(uids, new GameCommand(EntityOrders.Type.DEPLOY, clickedItem.getUID()));
                    }
                }
            } else { // Player clicked on the ground
                // identify selected items from players team that can move
                for (int i = game.getSelectedItems().size() - 1; i >= 0; i--) {
                    Entity item = game.getSelectedItems().get(i);
                    if (item.getTeam() == game.getTeam() && (item instanceof Vehicle || item instanceof Aircraft)) {
                        uids.add(item.getUID());
                    }
                }
                // then command them to move to the clicked location
                if (!uids.isEmpty()) {
                    int toX = gameX / game.getGridSize();
                    int toY = gameY / game.getGridSize();
                    game.sendCommand(uids, new GameCommand(EntityOrders.Type.MOVE, toX, toY));
                }
            }
    	}
    }
    
}
